package com.cgs.query.service.impl

import com.cgs.query.config.QueryConfigurationAware
import com.cgs.query.configuration.QueryConfiguration
import com.cgs.query.domain.BoundSql
import com.cgs.query.domain.ExecutionObj
import com.cgs.query.exception.QueryException
import com.cgs.query.handler.*
import com.cgs.query.service.IExecutionObjService
import com.cgs.query.service.jpa.ExecutionObjRepository
import com.cgs.query.util.GsonUtils
import com.cgs.query.util.Utils
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
open class ExecutionObjServiceImpl : IExecutionObjService, UserInfoHolderAware, QueryConfigurationAware, SecurityAdapter {

    @Autowired
    private lateinit var executionObjRepository: ExecutionObjRepository
    private lateinit var userInfoHolder: UserInfoHolder
    private lateinit var queryConfiguration: QueryConfiguration

    override fun findByProjectNameAndWholeName(projectName: String, wholeName: String): ExecutionObj? {
        val aliasAndDefineName = Utils.extractAliasAndDefineName(wholeName)
        return executionObjRepository.findByDefineNameAndAliasAndProjectName(aliasAndDefineName.defineName, aliasAndDefineName.alias, projectName)
    }

    override fun findById(id: String): ExecutionObj? {
        return executionObjRepository.findOne(id)
    }

    override fun findByDefineId(id: String): Map<String, ExecutionObj> {
        val executionObjects = executionObjRepository.findByDefineId(id)
        return executionObjects.map { it.version to it }.toMap()
    }

    override fun findByDefineIds(ids: Iterable<String>): List<ExecutionObj> {
        return executionObjRepository.findByDefineIdIn(ids)
    }

    @Permission
    override fun save(executionObj: ExecutionObj): ExecutionObj {
        return executionObjRepository.save(executionObj.apply { userId = userInfoHolder.getUserId() })
    }

    override fun save(executionObjs: Iterable<ExecutionObj>) {
        executionObjRepository.save(executionObjs)
    }

    override fun findByDefineIdAndVersion(id: String, version: String): ExecutionObj {
        return executionObjRepository.findByDefineIdAndVersion(id, version)
    }

    @Permission
    override fun delete(id: String) {
        executionObjRepository.delete(id)
    }

    override fun delete(ids: Iterable<String>) {
        executionObjRepository.deleteInBatch(ids.map { ExecutionObj(it)})
    }

    override fun setUserInfoHolder(userInfoHolder: UserInfoHolder) {
        this.userInfoHolder = userInfoHolder
    }

    override fun rendered(id: String, param: Map<String, Any?>): BoundSql {
        return queryConfiguration.parameterHandler.resolve(findById(id) ?: throw QueryException("找不到该执行对象:$id"), param)
    }

    override fun rendered(executionObj: ExecutionObj): BoundSql {
        val json = if (executionObj.previewParams.isNullOrBlank())
            "{}"
        else
            executionObj.previewParams
        val params: Map<*, *>? = GsonUtils.gson.fromJson(json, Map::class.java)
        return queryConfiguration.parameterHandler.resolve(executionObj, params as Map<String, Any?>)
    }

    override fun setQueryConfiguration(queryConfiguration: QueryConfiguration) {
        this.queryConfiguration = queryConfiguration
    }

    override fun adapter(methodName: String, args: Array<Any?>): PermissionObject? {
        return when (methodName) {
            "save" -> {
                val executionObj = (args[0] as ExecutionObj)
                return if (executionObj.id.isNotBlank()) {
                    PermissionObject(null).apply {
                        this.userId = executionObj.userId
                        this.obj = executionObj
                        this.type = "executionObj"
                    }
                } else {
                    null
                }
            }
            "delete" -> {
                val executionObj = findById(args[0] as String)
                PermissionObject(null).apply {
                    this.userId = executionObj?.userId ?: ""
                    this.obj = executionObj
                    this.type = "executionObj"
                }
            }
            else -> null
        }
    }

}